// main application object
var App = {}

// in acest obiect sunt stocate clasele care modeleaza anumite functionalitati generale
var Models = {}

////////////////////////////////////////////////////////////////////////////////
//===== CONFIRMATION MESSAGE ===================================================
////////////////////////////////////////////////////////////////////////////////

Models.ConfirmationMessage = Module.extend(
{
    _options:
    {
        // the message will automatically be hidden after this amount of miliseconds
        hideAfter: 3000,
        // hide the message on click anywhere in the document
        hideOnClick: true,
        // hide with fadeOut effect; this option specifies the effect duration
        fadeOut: 500,
        errorClass: 'error',
        message: null
    },

    _$textContent: null,

    _timeout: null,

    _init: function()
    {
        var self = this;

        self._$textContent = self.getElement().find( '.content' );

        // set the default message
        self._setMessage( self._options.message );

        if ( self._options.hideOnClick )
        {
            // hide the message on click
            $.documentEvent( self.getElement(), function(){
                self.hide();
            })
        }
    },

    hide: function()
    {
        // reset the timer
        clearTimeout( this._timeout );
        // hide the message
        this.getElement()
                .fadeOut( this._options.fadeOut )
                .removeClass( this._options.errorClass );
    },

    show: function( message, timeout, error )
    {
        var self = this;

        if ( (typeof error == 'undefined' || !error) && (timeout === true) )
        {
            error = true;
            timeout = null;
        }

        self.hide();

        // set the text message
        this._setMessage( message );

        // show the message
        this.getElement().show();

        // check if the message is an error
        if ( typeof error != 'undefined' && error )
        {
            this.getElement().addClass(this._options.errorClass);
        }

        // hide it automatically
        if ( this._options.hideAfter )
        {
            this._timeout = setTimeout( function(){
                    self.hide();
                },
                (typeof timeout != 'undefined' && timeout) || this._options.hideAfter );
        }
    },

    showError: function( message )
    {
        this.show( message, 10000, true );
    },

    _setMessage: function( message )
    {
        if ( typeof message != 'undefined' && message != null )
        {
            this._$textContent.text( message );
        }
    }

});

////////////////////////////////////////////////////////////////////////////////
//===== OVERLAY ================================================================
////////////////////////////////////////////////////////////////////////////////

Models.Overlay = Module.extend(
{
    _init: function()
    {
        this.disable();
    },

    enable: function()
    {
        this.getElement().show();
    },

    disable: function()
    {
        this.getElement().hide();
    }
})


////////////////////////////////////////////////////////////////////////////////
//===== FORM ===================================================================
////////////////////////////////////////////////////////////////////////////////

/**
 * Model for a general ajax form
 * The constructor takes the form element object as the first argument
 *
 * HTML DEFINITION: <form id="..." request="[url]" action="javascript:void(0)" autocomplete="off">
 *
 * Methods:
 *    - submit
 *    - reset
 *    - getField
 *    - getFields
 *    - getFieldValue
 *    - setFieldValue
 *    - getData
 *
 * Custom handlers (defined in derived classes)
 *    - _onBeforeSubmit
 *    - _onReset
 *    - _onSuccess
 *    - _onErrors
 */
Models.Form = Module.extend(
{
    _options:
    {
        timeout: 6000,
        requestingClass: 'requesting',
        errorContainerSelector: '.error-container',
        fieldRowSelector: '.field-row'
    },

    _formName: null,
    _$submitButton: null,
    _inProgress: false,

    _init: function()
    {
        var self = this,
            $element = self.getElement();

        // setup the form name
        self._formName = $element.attr( 'name' );

        // define custom submit handler
        $element.submit( function( event ){
            event.preventDefault();
            self.submit();
        });

        // find the submit button and save it
        self._$submitButton = $element.find( ':submit' );

        // define custom reset handler
        $element.reset( function(){
            self._reset();
            // trigger the 'change' event on all form fields
            // - the default 'reset' event is executed after the custom handlers;
            //   because of this we launch the 'change' event on a separate thread after a small delay
            setTimeout( function(){
                    self.getFields().change();
                },
                1);
        });
    },

    submit: function()
    {
        var self = this,
            $element = self.getElement();

        if ( self._inProgress ) return;

        self._start();

        // call custom 'beforeSubmit' handler
        self._onBeforeSubmit();

        $.ajax({
            url:        $element.attr('request'),
            timeout:    self.getOption('timeout'),
            async:      false,
            type:       'post',
            data:       self.getData(),
            success:    function( response, statusText, xhr )
            {
                // reset errors
                self._clearErrors();

                // exista erori ?
                var responseType = xhr.getResponseHeader( 'Response-Type' );
                if ( responseType == 'validation-errors' )
                {
                    self._renderErrors(response);
                    // call custom 'error' handler
                    self._onErrors(response);
                }
                // nu exista erori:
                else if (response.success)
                {
                    // reset the Widget
                    self.reset();

                    // call 'success' handler
                    self._onSuccess(response);
                }
                else
                {
                    App.ConfirmationMessage.showError( 'Server error' );
                }

            },
            complete: function( response, statusText, xhr )
            {
                self._end();
            }
        });
    },

    reset: function()
    {
        this.getElement().reset();
    },

    /**
     * Returns a field as jQuery object
     * @param fieldName The value of 'name' attribute of the field
     * @return jQuery
     */
    getRawField: function( fieldName, filterSelector )
    {
        if ( typeof filterSelector == 'undefined' )
        {
            filterSelector = '';
        }

        var $field = this.getElement().find( '[name="' + fieldName + '"]' + filterSelector );

        return $field;
    },

    /**
     * Returns a field as jQuery object
     * @param fieldName The value of 'name' attribute of the field, excluding the form name
     * @return jQuery
     */
    getField: function( fieldName, filterSelector )
    {
        fieldName = this._formName + '[' + fieldName + ']';

        var $field = this.getRawField( fieldName, filterSelector );

        return $field;
    },

    /**
     * Sets the value of a field
     * @param field The value of 'name' attribute of the field
     * @param value The value to be set
     */
    setFieldValue: function( field, value )
    {
        // set the value and trigger the 'change' event
        this.getField( field )
                .val( value )
                .change();
    },

    /**
     * Gets the value of a field
     * @param field The value of 'name' attribute of the field
     */
    getFieldValue: function( field )
    {
        // set the value and trigger the 'change' event
        this.getField( field )
                .val();
    },

    getFields: function()
    {
        var self = this,
            arrayData = self.getElement().serializeArray(),
            $fields = $();

        // there can be multiple fields having the same name attribute
        var fieldsCount = {};

        $.each( arrayData, function( index ){
            var fieldName = this.name;

            if ( typeof fieldsCount[fieldName] == 'undefined' )
            {
                fieldsCount[fieldName] = 0;
            }

            var $field = self.getRawField( fieldName, ':eq(' + ( fieldsCount[fieldName]++ ) + ')' );

            $fields = $fields.add( $field );
        })

        return $fields;
    },

    getData: function()
    {
        var data = [];

        // remove values of not visible fields
        $.each(this.getFields(), function(){
            var $field      = $(this),
                fieldName   = $field.attr('name'),
                fieldValue  = $field.val();

            // if the field hasn't any hidden parents, it is included in the result
            if ( !$field.parents(':hidden').length )
            {
                data.push( {name: fieldName, value: fieldValue} );
            }
        })

        return data;
    },

    /**
     * These event handlers will be defined in derived classes
     */
    _onBeforeSubmit: function() {},
    _onReset: function() {},
    _onSuccess: function() {},
    _onErrors: function() {},

    /**
     * Marks the start of the request
     */
    _start: function()
    {
        this._$submitButton
                .attr( 'disabled', 'disabled' )
                .addClass( this.getOption('requestingClass') );

        App.Overlay.enable();

        this._inProgress = true;
    },

    /**
     * Marks the end of the request
     */
    _end: function()
    {
        this._$submitButton
                .removeAttr( 'disabled' )
                .removeClass( this.getOption('requestingClass') );

        App.Overlay.disable();

        this._inProgress = false;
    },

    _clearErrors: function()
    {
        this.getElement().find( this.getOption('errorContainerSelector') ).html('');
    },

    _renderErrors: function( errors )
    {
        var self = this,
            $element = self.getElement();

        // display the validation error messages
        // daca a fost primit parametrul 'refresh', atunci se reincarca pagina
        if ( errors.refresh )
        {
            window.location.reload();
            throw new Error( 'Reloading the page!' );
        }

        // erori specifice unui camp din formular
        $.each( errors.fields, function( key ){
            // find error container; 'key' represents the value of the name attribute
            var $field = self.getRawField( key );
            $field.parents( self.getOption('fieldRowSelector') )
                    .find( self.getOption('errorContainerSelector') )
                    .text( this.valueOf() );
        });

        // erori globale (specifice formularului)
        var $globalErrors = $element.children( self.getOption('errorContainerSelector') );
        if ( $globalErrors.length )
        {
            $.each( errors.global, function(){
                $globalErrors.append( this.valueOf() + '<br />' );
            });
        }

    },

    _reset: function()
    {
        this._clearErrors();
        this._onReset();
    }

})

////////////////////////////////////////////////////////////////////////////////
//===== TABS ===================================================================
////////////////////////////////////////////////////////////////////////////////

Models.Tabs = Module.extend(
{
    _init: function()
    {
        this.getElement().tabs( this.getOptions() );
    },

    /**
     * Select a tab
     */
    select: function( tabIndex )
    {
        this.getElement().tabs( 'select', tabIndex );
    }

})